Fixed relocation types and added GOT supports. Change-Id: I7803a4f6a52a237b16c67adc09948705d4fc5533
diff --git a/Android.mk b/Android.mk index c1d41d2..014ef83 100644 --- a/Android.mk +++ b/Android.mk
@@ -35,6 +35,7 @@ lib/ELFTypes.cpp \ lib/MemChunk.cpp \ lib/StubLayout.cpp \ + lib/GOT.cpp \ utils/raw_ostream.cpp \ utils/rsl_assert.cpp \ utils/helper.cpp \
diff --git a/SConstruct b/SConstruct index 03a47d3..1fa5b63 100644 --- a/SConstruct +++ b/SConstruct
@@ -94,6 +94,7 @@ 'lib/ELFTypes.cpp', 'lib/MemChunk.cpp', 'lib/StubLayout.cpp', + 'lib/GOT.cpp', 'utils/helper.cpp', 'utils/raw_ostream.cpp', 'utils/rsl_assert.cpp',
diff --git a/include/GOT.h b/include/GOT.h new file mode 100644 index 0000000..8489b29 --- /dev/null +++ b/include/GOT.h
@@ -0,0 +1,37 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GOT_H +#define GOT_H + +#include "utils/rsl_assert.h" +#include "ELF.h" + +#if defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) +#define GP_OFFSET ((int)0x8000) +#define GOT_SIZE (1 << 16) // bytes +#define GOT_ENTRY_SIZE 4 // bytes +#define NUM_OF_GOT_ENTRY (GOT_SIZE/GOT_ENTRY_SIZE) +#else +#define GOT_SIZE 4 // bytes +#define GOT_ENTRY_SIZE 4 // bytes +#define NUM_OF_GOT_ENTRY (GOT_SIZE/GOT_ENTRY_SIZE) +#endif + +void *got_address(); +int search_got(int symbol_index, void *addr, uint8_t bind_type); + +#endif // GOT_H
diff --git a/include/impl/ELFObject.hxx b/include/impl/ELFObject.hxx index 83a1bc6..e491e15 100644 --- a/include/impl/ELFObject.hxx +++ b/include/impl/ELFObject.hxx
@@ -22,6 +22,7 @@ #include "ELFSection.h" #include "ELFSectionHeaderTable.h" #include "StubLayout.h" +#include "GOT.h" #include "ELF.h" #include <llvm/ADT/SmallVector.h> @@ -370,7 +371,10 @@ Inst_t A = (Inst_t)(uintptr_t)*inst; Inst_t S = (Inst_t)(uintptr_t)sym->getAddress(); - if (S == 0) { + bool need_stub = false; + + if (S == 0 && strcmp (sym->getName(), "_gp_disp") != 0) { + need_stub = true; S = (Inst_t)(uintptr_t)find_sym(context, sym->getName()); sym->setAddress((void *)S); } @@ -380,31 +384,98 @@ rsl_assert(0 && "Not implemented relocation type."); break; - case R_MIPS_HI16: - A = A & 0xFFFF; - // FIXME: We just support addend = 0. - rsl_assert(A == 0 && "R_MIPS_HI16 addend is not 0."); - *inst |= (((S + 0x8000) >> 16) & 0xFFFF); + case R_MIPS_NONE: + case R_MIPS_JALR: // ignore this break; - case R_MIPS_LO16: + case R_MIPS_16: + *inst &= 0xFFFF0000; A = A & 0xFFFF; - // FIXME: We just support addend = 0. - rsl_assert(A == 0 && "R_MIPS_LO16 addend is not 0."); - *inst |= (S & 0xFFFF); - break; - - case R_MIPS_26: - A = A & 0x3FFFFFF; - // FIXME: We just support addend = 0. - rsl_assert(A == 0 && "R_MIPS_26 addend is not 0."); - *inst |= ((S >> 2) & 0x3FFFFFF); - rsl_assert((((P + 4) >> 28) != (S >> 28)) && "Cannot relocate R_MIPS_26 due to differences in the upper four bits."); + A = S + (short)A; + rsl_assert(A >= -32768 && A <= 32767 && "R_MIPS_16 overflow."); + *inst |= (A & 0xFFFF); break; case R_MIPS_32: *inst = S + A; break; + + case R_MIPS_26: + *inst &= 0xFC000000; + if (need_stub == false) { + A = (A & 0x3FFFFFF) << 2; + if (sym->getBindingAttribute() == STB_LOCAL) { // local binding + A |= ((P + 4) & 0xF0000000); + A += S; + *inst |= ((A >> 2) & 0x3FFFFFF); + } + else { // external binding + if (A & 0x08000000) // Sign extend from bit 27 + A |= 0xF0000000; + A += S; + *inst |= ((A >> 2) & 0x3FFFFFF); + if (((P + 4) >> 28) != (A >> 28)) { // far local call + void *stub = text->getStubLayout()->allocateStub((void *)A); + rsl_assert(stub && "cannot allocate stub."); + sym->setAddress(stub); + S = (int32_t)stub; + *inst |= ((S >> 2) & 0x3FFFFFF); + rsl_assert(((P + 4) >> 28) == (S >> 28) && "stub is too far."); + } + } + } + else { // shared-library call + A = (A & 0x3FFFFFF) << 2; + rsl_assert(A == 0 && "R_MIPS_26 addend is not zero."); + void *stub = text->getStubLayout()->allocateStub((void *)S); + rsl_assert(stub && "cannot allocate stub."); + sym->setAddress(stub); + S = (int32_t)stub; + *inst |= ((S >> 2) & 0x3FFFFFF); + rsl_assert(((P + 4) >> 28) == (S >> 28) && "stub is too far."); + } + break; + + case R_MIPS_HI16: + *inst &= 0xFFFF0000; + A = A & 0xFFFF; + if (strcmp (sym->getName(), "_gp_disp") == 0) { + S = (int)got_address() + GP_OFFSET - (int)P; + sym->setAddress((void *)S); + } + *inst |= (((S + (A << 16) + (int)0x8000) >> 16) & 0xFFFF); + break; + + case R_MIPS_LO16: + *inst &= 0xFFFF0000; + A = A & 0xFFFF; + if (strcmp (sym->getName(), "_gp_disp") == 0) { + S = (Inst_t)sym->getAddress(); + } + *inst |= ((S + A) & 0xFFFF); + // We assume the addend of R_MIPS_LO16 won't affect R_MIPS_HI16. + // If not, we have troubles. + if (((S + (short)A + (int)0x8000) >> 16) != ((S + (int)0x8000) >> 16)) + rsl_assert("AHL cannot be calculated correctly."); + break; + + case R_MIPS_GOT16: + case R_MIPS_CALL16: + { + *inst &= 0xFFFF0000; + A = A & 0xFFFF; + // FIXME: We just support addend = 0. + rsl_assert(A == 0 && "R_MIPS_GOT16/R_MIPS_CALL16 addend is not 0."); + int got_index = search_got((int)rel->getSymTabIndex(), (void *)S, + sym->getBindingAttribute()); + int got_offset = (got_index << 2) - GP_OFFSET; + *inst |= (got_offset & 0xFFFF); + } + break; + + case R_MIPS_GPREL32: + *inst = A + S - ((int)got_address() + GP_OFFSET); + break; } } } diff --git a/include/impl/ELFSectionRelTable.hxx b/include/impl/ELFSectionRelTable.hxx index e83799c..4c2417d 100644 --- a/include/impl/ELFSectionRelTable.hxx +++ b/include/impl/ELFSectionRelTable.hxx
@@ -93,7 +93,7 @@ switch (obj->getHeader()->getMachine()) { case EM_ARM: { - std::set<word_t> sym_index_set; + std::set<uint32_t> sym_index_set; for (size_t i = 0; i < size(); ++i) { ELFRelocTy *rel = table[i]; @@ -106,9 +106,23 @@ return sym_index_set.size(); } + case EM_MIPS: + { + std::set<uint32_t> sym_index_set; + + for (size_t i = 0; i < size(); ++i) { + ELFRelocTy *rel = table[i]; + + if (rel->getType() == R_MIPS_26) { + sym_index_set.insert(rel->getSymTabIndex()); + } + } + + return sym_index_set.size(); + } + case EM_386: case EM_X86_64: - case EM_MIPS: return 0; default: diff --git a/include/impl/ELFSymbol.hxx b/include/impl/ELFSymbol.hxx index 176428a..01c84cc 100644 --- a/include/impl/ELFSymbol.hxx +++ b/include/impl/ELFSymbol.hxx
@@ -199,8 +199,12 @@ } break; - case SHN_ABS: case SHN_UNDEF: +#if defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) + if (strcmp(getName(), "_gp_disp") == 0) // OK for MIPS + break; +#endif + case SHN_ABS: case SHN_XINDEX: rsl_assert(0 && "STT_OBJECT with special st_shndx."); break;
diff --git a/lib/GOT.cpp b/lib/GOT.cpp new file mode 100644 index 0000000..fbafaea --- /dev/null +++ b/lib/GOT.cpp
@@ -0,0 +1,74 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include "GOT.h" + +void *got_symbol_addresses[NUM_OF_GOT_ENTRY]; +int got_symbol_indexes[NUM_OF_GOT_ENTRY]; +size_t got_symbol_count = 0; + +void *got_address() +{ + return &got_symbol_addresses[0]; +} + +#if defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) + +int search_got(int symbol_index, void *addr, uint8_t bind_type) +{ + size_t i; + + // For local symbols (R_MIPS_GOT16), we only store the high 16-bit value + // after adding 0x8000. + if (bind_type == STB_LOCAL) + addr = (void *)(((int)addr + 0x8000) & 0xFFFF0000); + + for (i = 0; i < got_symbol_count; i++) { + if (got_symbol_indexes[i] == symbol_index) { + if (bind_type == STB_LOCAL) { + // Check if the value is the same for local symbols. + // If yes, we can reuse this entry. + // If not, we continue searching. + if (got_symbol_addresses[i] == addr) { + return i; + } + } + else { + // The value must be the same for global symbols . + rsl_assert (got_symbol_addresses[i] == addr + && "MIPS GOT address error."); + return i; + } + } + } + + // Cannot find this symbol with correct value, so we need to create one + rsl_assert (got_symbol_count < NUM_OF_GOT_ENTRY && "MIPS GOT is full."); + got_symbol_indexes[got_symbol_count] = symbol_index; + got_symbol_addresses[got_symbol_count] = addr; + got_symbol_count++; + return (got_symbol_count - 1); +} + +#else + +int search_got(int symbol_index, void *addr, uint8_t bind_type) +{ + return 0; +} + +#endif